#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <limits.h>

//size of input file.

int size_of_file(char *INPUT){
    struct stat st;
    stat(INPUT, &st);
    int size = st.st_size;
    return size-1;
}

//check of correctness of format of input file. it must be CSV.

int correctness(char* input, int size){
    int i, r = 0;  
    for (i = 0; i < size; i++){
	if (input[i] == ','){
	    if (r == 0){
		r = 1;
	    }
	    else{
		return -1;
	    }
	}
	else{
	    if (input[i] == '-'){
		if ((i != 0) && (r != 1)){
		    return -1;
		}
		else{
		    r = 2;
		}
	    }
	    else{
		if ((input[i] <= '9') && (input[i] >= '0')){
		    r = 0;	  
		}
		else {
		    return -1;
		}
	    }
	}
    }
  
  //number can't begin with 0
  
    if (size > 1){
	if ((input[0] == '0') && (input[1] <= '9') && (input[1] >= '0')){
	    return -2;
	} 
    }
    if (size > 2){
	for (i = 0; i < size-2; i++){
	    if ((input[i] == ',') && (input[i+1] == '0') && (input[i+2] <= '9') && (input[i+2] >= '0')){
		return -2;
	    }
	}
    }
    return 0;
}

//mapping of input file and parsing arguments for sorting. return pointer on numbers, which will be sorted. the first element is number of arguments. 

int* parsed_arg(char *INPUT){
    char *map_input;      
    int fd = open(INPUT, O_RDONLY);
    if (fd == -1) {
	perror("ERROR of opening the output file for reading");
	exit(EXIT_FAILURE);
    }
    int size = size_of_file(INPUT);
    map_input = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0);
    if (map_input == MAP_FAILED){
	close(fd);
	perror("ERROR of mapping the output file ");
	exit(EXIT_FAILURE);
    }  
    if (correctness(map_input, size) == -1){
	close(fd);
	perror("ERROR! Input file isn't in CSV format. Call of help with command line argument '-h'.");
	exit(EXIT_FAILURE);
    }    
    if (correctness(map_input, size) == -2){
	close(fd);
	perror("ERROR! Number with more than 1 digit can't begin with 0. Call of help with command line argument '-h'.");
	exit(EXIT_FAILURE);
    }  
    char map_input_array[size];
    int num_of_arg = 1; //quantity of numbers in input
    int i;
    for (i = 0; i < size; i++){
	map_input_array[i] = map_input[i];
	if (map_input[i] == ','){
	    num_of_arg++;
	    if (num_of_arg == INT_MAX){
		close(fd);
		perror("ERROR! Quantity of numbers in input file is very big. Call of help with command line argument '-h'.");
		exit(EXIT_FAILURE);
	    }
	}
    }  
    if (munmap(map_input, size) == -1){
	perror("ERROR of unmapping the input file");
    }  
    char **arg;
    arg = malloc((num_of_arg+1) * sizeof(char*));
    arg[0] = strtok(map_input_array, ",");
    i = 1;
    while (arg[i-1] != NULL){
	arg[i] = strtok(NULL, ",");
	i++;
    }
    close(fd);
    int *M;
    M = malloc((num_of_arg+1) * sizeof(int));
    for (i=0; i < num_of_arg; i++){
	M[i+1] = strtol(arg[i], NULL, 10);
	if ((M[i+1] >= INT_MAX) || (M[i+1] <= INT_MIN)){
	    close(fd);
	    perror("ERROR! Call of help with command line argument '-h'.");
	    exit(EXIT_FAILURE);
	}
    }
    M[0] = num_of_arg;
    return M;
}

//get sorted numbers and write them in mapped output file in CSV format.

void writing(int *M, int num_of_arg, char *OUTPUT, char *INPUT){
    char *map_output;
    int size = size_of_file(INPUT);
    int fd = open(OUTPUT, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
    if (fd == -1){
	perror("ERROR of opening the output file for writing");
	exit(EXIT_FAILURE);
    }  
    int end = lseek(fd, size-1, SEEK_SET);  
    if (end == -1){
	close(fd);
	perror("ERROR of calling lseek() to stretch the output file");
	exit(EXIT_FAILURE);
    }  
    end = write(fd, "", 1); //stretching of output file  
    if (end != 1){
	close(fd);
	perror("ERROR of writing the last byte to the output file");
	exit(EXIT_FAILURE);
    }
    map_output = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);  
    if (map_output == MAP_FAILED){
	close(fd);
	perror("ERROR of mapping the output file");
	exit(EXIT_FAILURE);
    } 
    char str[6]; 
    int i;
    for (i = 1; i < num_of_arg+1; i++){
	snprintf(str, 6 , "%d", M[i]);	
	strcat(map_output, str);
	if (i != num_of_arg){
	    strcat(map_output, ",");
	}  
    }
//    printf("%s\n", map_output);
    if (munmap(map_output, size) == -1) {
	perror("ERROR of unmapping the output file");
    }
    close(fd);
}
  
  
void helpYourself(int *m, int left, int right, int mid) {
    int i=0, j=0;
    int l = mid-left;
    int *ml = m+left, *mr = m+mid;
    int r = right-mid;
    int *s = malloc((l+r)*sizeof(int));
    while ((i<l)&&(j<r)) {
	s[i+j] = (ml[i]<mr[j])?ml[i++]:mr[j++];
    }
    while (i<l) {
	s[i+j] = ml[i++];
    }
    while (j<r) {
	s[i+j] = mr[j++];
    }
    memcpy(m+left, s, (right-left)*sizeof(int));
    free(s);
}

void myMysteriousSort(int *m, int left, int right) {
    if ((right-left) < 2) {
	return;
    }
    int mid = (right+left)/2;
    myMysteriousSort(m, left, mid);
    myMysteriousSort(m, mid, right);
    helpYourself(m, left, right, mid);
}

int main(int argc, char **argv){
    char INPUT[50], OUTPUT[50];
    if ((argc == 2) && (strcmp(argv[1],"-h")) == 0){
	printf("\nHELP\n\nThe first command line argument is path of input file, the second - output file.\nFormat of input file is CSV.\nAll numbers in input file and their quantity must be integer.\n\n");
	return 0;
    }
    else{  
	if (argc != 3){
	    printf("Call of help with command line argument '-h'.\n");
	    return 0; 
	}
	else{
	    strcpy(INPUT, argv[1]);
	    strcpy(OUTPUT, argv[2]);
	}
    }  
    int *M = parsed_arg(INPUT);
    int num_of_arg = M[0];  
    myMysteriousSort(M, 1, num_of_arg+1);  
    writing(M, num_of_arg, OUTPUT, INPUT);
    free(M); 
    return 0;
}